function openFile(accept: string) {
    return new Promise<{ filename: string, fileContent: string; }>(resolve => {
        const inputTag = document.createElement('input');
        inputTag.type = 'file';
        inputTag.accept = accept;
        inputTag.click();
        const callback = () => {
            inputTag.removeEventListener('change', callback);
            if (inputTag.files) {
                const files = [...inputTag.files];
                if (files.length) {
                    const file = files[0];
                    const filename = file.name;
                    file.text().then(fileContent => {
                        resolve({ filename, fileContent });
                    });
                }
            } else {
                window.alert('发生了一些问题');
            }
        };
        inputTag.addEventListener('change', callback);
    });
}
(() => {
    const loadButton = document.querySelector<HTMLButtonElement>('button#loadButton');
    const blankButton = document.querySelector<HTMLButtonElement>('button#blankButton');
    const canvas = document.querySelector<HTMLCanvasElement>('canvas#canvas');
    if (loadButton && blankButton && canvas) {
        interface GeoPoint {
            x: number;
            y: number;
        };
        interface Point extends GeoPoint {
            label: number;
            acceptable: number[];
        }
        const toPoint = (v: string) => {
            const list = v.trim().split(/\s+/).map(d => +d);
            const [x, y, label, ...acceptable] = list;
            return { x, y, label, acceptable };
        };
        /**
         * @brief https://www.yisu.com/zixun/171263.html
         */
        const gaussBlur = (imgData: ImageData) => {
            let pixes = imgData.data;
            let width = imgData.width;
            let height = imgData.height;
            let gaussMatrix = [],
                gaussSum = 0,
                x, y,
                r, g, b, a,
                i, j, k, len;

            let radius = 20;
            let sigma = 10;

            a = 1 / (Math.sqrt(2 * Math.PI) * sigma);
            b = -1 / (2 * sigma * sigma);
            //生成高斯矩阵
            for (i = 0, x = -radius; x <= radius; x++, i++) {
                g = a * Math.exp(b * x * x);
                gaussMatrix[i] = g;
                gaussSum += g;

            }

            //归一化, 保证高斯矩阵的值在[0,1]之间
            for (i = 0, len = gaussMatrix.length; i < len; i++) {
                gaussMatrix[i] /= gaussSum;
            }
            //x 方向一维高斯运算
            for (y = 0; y < height; y++) {
                for (x = 0; x < width; x++) {
                    r = g = b = a = 0;
                    gaussSum = 0;
                    for (j = -radius; j <= radius; j++) {
                        k = x + j;
                        if (k >= 0 && k < width) {//确保 k 没超出 x 的范围
                            //r,g,b,a 四个一组
                            i = (y * width + k) * 4;
                            r += pixes[i] * gaussMatrix[j + radius];
                            g += pixes[i + 1] * gaussMatrix[j + radius];
                            b += pixes[i + 2] * gaussMatrix[j + radius];
                            // a += pixes[i + 3] * gaussMatrix[j];
                            gaussSum += gaussMatrix[j + radius];
                        }
                    }
                    i = (y * width + x) * 4;
                    // 除以 gaussSum 是为了消除处于边缘的像素, 高斯运算不足的问题
                    // console.log(gaussSum)
                    pixes[i] = r / gaussSum;
                    pixes[i + 1] = g / gaussSum;
                    pixes[i + 2] = b / gaussSum;
                    // pixes[i + 3] = a ;
                }
            }
            //y 方向一维高斯运算
            for (x = 0; x < width; x++) {
                for (y = 0; y < height; y++) {
                    r = g = b = a = 0;
                    gaussSum = 0;
                    for (j = -radius; j <= radius; j++) {
                        k = y + j;
                        if (k >= 0 && k < height) {//确保 k 没超出 y 的范围
                            i = (k * width + x) * 4;
                            r += pixes[i] * gaussMatrix[j + radius];
                            g += pixes[i + 1] * gaussMatrix[j + radius];
                            b += pixes[i + 2] * gaussMatrix[j + radius];
                            // a += pixes[i + 3] * gaussMatrix[j];
                            gaussSum += gaussMatrix[j + radius];
                        }
                    }
                    i = (y * width + x) * 4;
                    pixes[i] = r / gaussSum;
                    pixes[i + 1] = g / gaussSum;
                    pixes[i + 2] = b / gaussSum;
                }
            }
            return imgData;
        };
        const paint = (data: Point[], all: boolean) => {
            const context = canvas.getContext('2d');
            if (!context) {
                throw new Error('Cannot get context 2d.');
            }
            const width = canvas.width;
            const height = canvas.height;
            context.clearRect(0, 0, width, height);
            context.fillStyle = 'white';
            context.getImageData;
            context.fillRect(0, 0, width, height);
            if (data.length === 0) {
                return;
            }
            const piece = 59;
            if (all) {
                for (const [i, point] of data.entries()) {
                    if (all && point.label !== -1) {
                        continue;
                    }
                    const { x, y } = point;
                    context.save();
                    const rgb = point.acceptable.map(d => Math.min(255, (Math.floor(d * 100) + 155)));
                    context.fillStyle = `rgba(${rgb.join(',')})`;
                    context.fillRect(x * width - width / piece / 2, y * height - height / piece / 2, width / piece + 1, height / piece + 1);
                    context.restore();
                }
                context.putImageData(gaussBlur(context.getImageData(0, 0, width, height)), 0, 0);
            }
            for (const point of data) {
                if (point.label === -1) {
                    continue;
                }
                context.save();
                const rgb = point.acceptable.map(d => Math.min(255, (Math.floor(d * 175) + 80)));
                context.fillStyle = ['red', 'green', 'blue'][point.label];
                context.beginPath();
                context.arc(point.x * width, point.y * height, 5, 0, 2 * Math.PI);
                context.fill();
                if (!all) {
                    context.fillStyle = `rgba(${rgb.join(',')},0.03)`;
                    context.beginPath();
                    context.arc(point.x * width, point.y * height, 12, 0, 2 * Math.PI);
                    context.fill();
                }
                context.restore();
            }
        };
        let allFlag = true;
        let points = [] as Point[];
        paint([], allFlag);
        loadButton.addEventListener('click', () => {
            openFile('.javaml-point.txt').then(({ fileContent }) => {
                const lines = fileContent.split('\n').map(line => line.trim()).filter(line => line !== '');
                points = lines.map(line => toPoint(line));
                paint(points, allFlag);
            });
        });
        blankButton.addEventListener('click', () => {
            allFlag = !allFlag;
            paint(points, allFlag);
        });
    }
})();
